package behaviorTree

import (
	"gitee.com/kingzyt/common/log"
	"sync/atomic"
)

type IRuntimeBTData interface {
	// 可以直接使用BaseBTRuntimeData提供的实现 ////////////////
	GetName() string

	GetRuntimeNodeStack() *NodeStack

	SetData(key string, val interface{})
	GetData(key string) interface{}

	ClearRunningT()
	AddRunningT(dt int64)
	GetRunningT() int64
	GetCurRunningNodeName() string

	Pause(pause bool)
	IsPaused() bool

	Abort(reason string)
	IsAborted() bool
	CheckAbort()
	////////////////////////////////////////////////

	//需要自定义的接口
	// 对于各种不同的节点类型,全局设置不同的超时时间
	GetRunningTimeoutT(runningNodeName string, runningNodeType NodeType) int64
	/*
		只有Action和ActionWait的节点是允许有RUNNING态的,也就这两种节点可能会调用到这个接口
		因为一般的Action都是需要一定执行时间而且很可能是异步的,
		这样在Action的执行前后需要自主标定状态是running/success/failure,
		bt在update的时候根据提供的状态来决定是否继续等在节点上
	*/
	ActionRunningCheck(name string) NodeState
}

// does not implement all api of IRuntimeBTData, e.g. ActionRunningCheck, GetRunningTimeoutT
type BaseBTRuntimeData struct {
	name        string
	stack       *NodeStack
	datas       map[string]interface{}
	accRunningT int64
	aborted     int32
	paused      int32
}

func NewBaseBTRuntimeData(name string) *BaseBTRuntimeData {
	return &BaseBTRuntimeData{
		name:  name,
		stack: NewNodeStack(),
		datas: map[string]interface{}{},
	}
}

func (self *BaseBTRuntimeData) GetName() string {
	return self.name
}

func (self *BaseBTRuntimeData) GetRuntimeNodeStack() *NodeStack {
	return self.stack
}
func (self *BaseBTRuntimeData) InitSetData(key string, val interface{}) {
	_, existed := self.datas[key]
	if existed {
		panic("SetData is dup, key:" + key)
	}
	self.datas[key] = val
}
func (self *BaseBTRuntimeData) SetData(key string, val interface{}) {
	self.datas[key] = val
}
func (self *BaseBTRuntimeData) GetData(key string) interface{} {
	return self.datas[key]
}

func (self *BaseBTRuntimeData) ClearRunningT() {
	atomic.StoreInt64(&self.accRunningT, 0)
}
func (self *BaseBTRuntimeData) AddRunningT(dt int64) {
	atomic.AddInt64(&self.accRunningT, dt)
}
func (self *BaseBTRuntimeData) GetRunningT() int64 {
	return atomic.LoadInt64(&self.accRunningT)
}

func (self *BaseBTRuntimeData) Pause(pause bool) {
	if pause {
		atomic.StoreInt32(&self.paused, 1)
	} else {
		atomic.StoreInt32(&self.paused, 0)
	}
}
func (self *BaseBTRuntimeData) IsPaused() bool {
	return atomic.LoadInt32(&self.paused) != 0
}

func (self *BaseBTRuntimeData) Abort(reason string) {
	atomic.StoreInt32(&self.aborted, 1)
	log.Info(self.name, log.Trace, "Aborted: %s", reason)
}
func (self *BaseBTRuntimeData) IsAborted() bool {
	return atomic.LoadInt32(&self.aborted) != 0
}
func (self *BaseBTRuntimeData) CheckAbort() {
	if atomic.CompareAndSwapInt32(&self.aborted, 1, 0) {
		self.stack.Clear()
	}
}

func (self *BaseBTRuntimeData) GetCurRunningNodeName() string {
	node, _, _ := self.stack.GetTopNode()
	if node != nil {
		return node.GetName()
	}
	return ""
}
